package de.fzi.kasma.learner.genetic;


import ec.BreedingPipeline;
import ec.EvolutionState;
import ec.Individual;
import ec.util.Parameter;

public class RLCrossoverPipeline extends BreedingPipeline {

	
    /**
	 * 
	 */
	private static final long serialVersionUID = 3266816788458581701L;
	
	
	public static final String P_TOSS = "toss";
    public static final String P_CROSSOVER = "xover";
    public static final String P_CROSSOVERPROB = "crossover-prob";
    public static final int INDS_PRODUCED = 2;
    public static final int NUM_SOURCES = 2;

    /** Should the pipeline discard the second parent after crossing over? */
    public boolean tossSecondParent;
    
    /** What is the probability of a rule migrating? */
    public float CrossProbability;

    /** Temporary holding place for parents */
    RLIndividual parents[];

    public RLCrossoverPipeline() { 
    	parents = new RLIndividual[2]; 
    }


    
	@Override
	public int numSources() {
		// TODO Auto-generated method stub
		return NUM_SOURCES;
	}

	@Override
	public int produce(int min, int max, int start, int subpopulation,
			Individual[] inds, EvolutionState state, int thread) {
		
		// TODO We should go over this function again. 
		
	     // how many individuals should we make?
        int n = (tossSecondParent? 1 : INDS_PRODUCED);
        if (n < min) n = min;
        if (n > max) n = max;
//        SimpleInitializer initializer = ((SimpleInitializer)state.initializer);
    
        for(int q=start;q<n+start; /* no increment */)  // keep on going until we're filled up
            {
            // grab two individuals from our sources
            if (sources[0]==sources[1])  // grab from the same source
                {
                sources[0].produce(2,2,0,subpopulation,parents,state,thread);
                if (!(sources[0] instanceof BreedingPipeline))  // it's a selection method probably
                    { 
                    parents[0] = (RLIndividual)(parents[0].clone());
                    parents[1] = (RLIndividual)(parents[1].clone());
                    }
                }
            else // grab from different sources
                {
                sources[0].produce(1,1,0,subpopulation,parents,state,thread);
                sources[1].produce(1,1,1,subpopulation,parents,state,thread);
                if (!(sources[0] instanceof BreedingPipeline))  // it's a selection method probably
                    parents[0] = (RLIndividual)(parents[0].clone());
                if (!(sources[1] instanceof BreedingPipeline)) // it's a selection method probably
                    parents[1] = (RLIndividual)(parents[1].clone());
                }

            // at this point, parents[] contains our two selected individuals,
            // AND they're copied so we own them and can make whatever modifications
            // we like on them.

            // so we'll cross them over now.

            parents[0].preprocessIndividual(state,thread);
            parents[1].preprocessIndividual(state,thread);

//            if( parents[0].clauses.length != parents[1].clauses.length )
//                {
//                state.output.fatal( "The number of rule sets should be identical in both parents ( " +
//                    parents[0].clauses.length + " : " +
//                    parents[1].clauses.length + " )." );
//                }

            // TODO : Crossover here...

            RLIndividual[] twoIndividuals = new RLIndividual[2];
            
            while(true)
                {
                // create two Individuals (initially empty)
                for( int i = 0 ; i < 2 ; i++ )

                    twoIndividuals[i] = new RLIndividual();
                // split the first individual into two
                twoIndividuals = parents[0].splitIntoTwo( state, thread, twoIndividuals,CrossProbability);

                                    
                // split the second individual into two
                twoIndividuals = parents[1].splitIntoTwo( state, thread, twoIndividuals, 1 - CrossProbability);

                
                // ensure that there are enough rules
                if ((twoIndividuals[0].clauses != null) &&
                    (twoIndividuals[1].clauses != null))
                    break;
                    
                twoIndividuals = new RLIndividual[2];
                }
                
            // copy the results 
            parents[0].copyNoClone(twoIndividuals[1]);
            parents[1].copyNoClone(twoIndividuals[0]);
            
        
        parents[0].postprocessIndividual(state,thread);
        parents[1].postprocessIndividual(state,thread);

        parents[0].evaluated=false;
        parents[1].evaluated=false;
        
        // add them to the population
        inds[q] = parents[0];
        q++;
        if (q<n+start && !tossSecondParent)
            {
            inds[q] = parents[1];
            q++;
            }
        }
    return n;
    }

	@Override
	public Parameter defaultBase() {
		return RLDefaults.base().push(P_CROSSOVER);		
	}

	@Override
    public Object clone()
    {
    RLCrossoverPipeline c = (RLCrossoverPipeline)(super.clone());

    // deep-cloned stuff
    c.parents = (RLIndividual[]) parents.clone();

    return c;
    }



	@Override
	public void setup(EvolutionState state, Parameter base) {
		// TODO Auto-generated method stub
		super.setup(state, base);
		 Parameter def = defaultBase();
	        tossSecondParent = state.parameters.getBoolean(base.push(P_TOSS),
	            def.push(P_TOSS),false);
	        CrossProbability = state.parameters.getFloatWithDefault(base.push(P_CROSSOVERPROB),
	            def.push(P_CROSSOVERPROB),0.5f);
	        if (CrossProbability > 1.0 || CrossProbability < 0.0) 
	            state.output.fatal("Rule cross probability must be between 0 and 1",base.push(P_CROSSOVERPROB),
	                def.push(P_CROSSOVERPROB));
	}
	
	
	  /** Returns 2 (unless tossing the second sibling, in which case it returns 1) */
    public int typicalIndsProduced() { return (tossSecondParent? 1: INDS_PRODUCED); }
	


}
